//
//  MFAPMUtility.m
//  MFAPMManager
//
//  Created by zhang liyu on 2019/5/22.
//

#import "MFAPMUtility.h"

#import <AFNetworking/AFNetworkReachabilityManager.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CommonCrypto/CommonDigest.h>
#import <mach/mach.h>
#include <execinfo.h>

#import "MFAPMManager.h"
#import "MFAPMConfig.h"

@implementation MFAPMUtility

+ (NSString *)convertJSONStringFromData:(NSData *)data {
    
    if (!data) {
        return @"";
    }
    NSString *JSONString = @"";
    
    id JSONObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
    if ([NSJSONSerialization isValidJSONObject:JSONObject]) {
        JSONString = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:JSONObject options:NSJSONWritingPrettyPrinted error:NULL] encoding:NSUTF8StringEncoding];
        JSONString = [JSONString stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"];
    } else {
        JSONString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    }
    return JSONString;
}

+ (NSString *)convertJSONStringFromObject:(id)object{
    if (!object) {
        return @"";
    }
    NSString *JSONString = @"";
    if ([NSJSONSerialization isValidJSONObject:object]) {
        NSData *JSONData = [NSJSONSerialization dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:nil];
        JSONString = [[NSString alloc] initWithData:JSONData encoding:NSUTF8StringEncoding];
        JSONString = [JSONString stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"];
    }
    return JSONString;
}

+ (NSDictionary *)convertDictionaryFromJsonString:(NSString *)JSONString {
    if (JSONString.length == 0) {
        return [NSDictionary dictionary];
    }
    NSData *JSONData = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *err;
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:JSONData
                                                        options:NSJSONReadingMutableContainers
                                                          error:&err];
    if(err) {
        return [NSDictionary dictionary];
    }
    return [NSDictionary dictionaryWithDictionary:dic];
}

+ (NSData *)convertDataFromDictionary:(NSDictionary *)dict{
    if(![dict isKindOfClass:[NSDictionary class]] || dict.count == 0){
        return [NSData data];
    }
    NSData *data= [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
    return (data ? : [NSData data]);
}

+ (NSString *)convertJSONStringFromString:(NSString *)str{
    if(![str isKindOfClass:[NSString class]] || str.length == 0){
        return @"";
    }
    //去除传入字符串中的\n
    NSString *resultStr = [str stringByReplacingOccurrencesOfString:@"\n" withString:@""] ? : @"";
    //去除传入字符串中的\r
    resultStr = [resultStr stringByReplacingOccurrencesOfString:@"\r" withString:@""] ? : @"";
    
    return [NSString stringWithString:resultStr];
}

+ (NSData *)getHttpBodyFromRequest:(NSURLRequest *)request {
    
    NSData *httpBody;
    if (request.HTTPBody) {
        httpBody = request.HTTPBody;
    }else{
        if ([request.HTTPMethod isEqualToString:@"POST"]) {
            if (!request.HTTPBody) {
                NSInteger maxLength = 1024;
                uint8_t d[maxLength];
                NSInputStream *stream = request.HTTPBodyStream;
                NSMutableData *data = [[NSMutableData alloc] init];
                [stream open];
                BOOL endOfStreamReached = NO;
                while (!endOfStreamReached) {
                    NSInteger bytesRead = [stream read:d maxLength:maxLength];
                    if(bytesRead == 0){ //文件读取到了最后
                        endOfStreamReached = YES;
                    }else if (bytesRead == -1){ //文件读取出错
                        endOfStreamReached = YES;
                    }else if (!stream.streamError){ //文件读取正常
                        [data appendBytes:(void *)d length:bytesRead];
                    }
                }
                httpBody = [data copy];
                [stream close];
            }
        }
    }
    
    return httpBody;
}

+ (UIViewController *)currentTopViewController {
    
    id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
    UIViewController *rootViewController = appDelegate.window.rootViewController;
    
    return [self topViewControllerFromViewController:rootViewController];
}

+ (UIViewController *)topViewControllerFromViewController:(UIViewController *)viewController {
    
    if([viewController isKindOfClass:[UINavigationController class]]){
        
        UINavigationController *navigationController = (UINavigationController *)viewController;
        return [self topViewControllerFromViewController:navigationController.topViewController];
    }else if ([viewController isKindOfClass:[UITabBarController class]]){
        
        UITabBarController *tabbarController = (UITabBarController *)viewController;
        return [self topViewControllerFromViewController:tabbarController.selectedViewController];
    }else if (viewController.presentedViewController != nil && !viewController.presentedViewController.isBeingDismissed){
        
        return [self topViewControllerFromViewController:viewController.presentedViewController];
    }
    return viewController;
}

+ (NSArray *)currentViewControllerPath {
    
    id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
    UIViewController *rootViewController = appDelegate.window.rootViewController;
    
    return [self viewControllerPathFromViewController:rootViewController pathArr:[NSMutableArray array]];
}

+ (NSArray *)viewControllerPathFromViewController:(UIViewController *)viewController pathArr:(NSMutableArray *)pathArr{
    
    if([viewController isKindOfClass:[UINavigationController class]]){
        
        UINavigationController *navigationController = (UINavigationController *)viewController;
        
        [pathArr addObject:(NSStringFromClass([navigationController class]) ? : @"")];
        
        return [self viewControllerPathFromViewController:navigationController.topViewController pathArr:pathArr];
    }else if ([viewController isKindOfClass:[UITabBarController class]]){
        
        UITabBarController *tabbarController = (UITabBarController *)viewController;
        
        [pathArr addObject:(NSStringFromClass([tabbarController class]) ? : @"")];
        
        return [self viewControllerPathFromViewController:tabbarController.selectedViewController pathArr:pathArr];
    }else if (viewController.presentedViewController != nil && !viewController.presentedViewController.isBeingDismissed){
        
        [pathArr addObject:(NSStringFromClass([viewController.presentedViewController class]) ? : @"")];
        
        return [self viewControllerPathFromViewController:viewController.presentedViewController pathArr:pathArr];
    }
    [pathArr addObject:(NSStringFromClass([viewController class]) ? : @"")];
    
    return [NSArray arrayWithArray:pathArr];
}

+ (UIImage *)imageWithScreenshot {
    
    //目前这种截图的方式，只截取了最底层window和statusBar的截图，有键盘弹出时或者有其他window层级时并没有截图
    UIWindow *screenWindow = [[UIApplication sharedApplication].windows objectAtIndex:0];
    UIView *statusBarView = [[UIApplication sharedApplication] valueForKey:@"_statusBar"];
    
    //进行截图的绘制 scale原来设定为0
    UIGraphicsBeginImageContextWithOptions([UIScreen mainScreen].bounds.size, NO, [UIScreen mainScreen].scale);
    if ([screenWindow respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
        [screenWindow drawViewHierarchyInRect:screenWindow.bounds afterScreenUpdates:YES];
    }else{
        [screenWindow.layer renderInContext:UIGraphicsGetCurrentContext()];
    }
    
    if(statusBarView.layer){
        [statusBarView.layer renderInContext:UIGraphicsGetCurrentContext()];
    }
    
    //生成图片
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return viewImage;
}

+ (NSData *)imageDataWithScreenshot {
    //指定上传图片大小，单位B
    NSUInteger maxLength = [MFAPMManager sharedManager].config.maxScreenShotUploadImgSize * 1024;
    //按照指定大小压缩图片并返回
    return [self compressImage:[self imageWithScreenshot] withMaxLength:maxLength];
}

+ (NSData *)compressImage:(UIImage *)img withMaxLength:(NSUInteger)maxLength{
    // Compress by quality
    CGFloat compression = 1;
    NSData *data = UIImageJPEGRepresentation(img, compression);
    //NSLog(@"Before compressing quality, image size = %ld KB",data.length/1024);
    if (data.length < maxLength) return data;
    
    CGFloat max = 1;
    CGFloat min = 0;
    for (int i = 0; i < 6; ++i) {
        compression = (max + min) / 2;
        data = UIImageJPEGRepresentation(img, compression);
        //NSLog(@"Compression = %.1f", compression);
        //NSLog(@"In compressing quality loop, image size = %ld KB", data.length / 1024);
        if (data.length < maxLength * 0.9) {
            min = compression;
        } else if (data.length > maxLength) {
            max = compression;
        } else {
            break;
        }
    }
    //NSLog(@"After compressing quality, image size = %ld KB", data.length / 1024);
    if (data.length < maxLength) return data;
    UIImage *resultImage = [UIImage imageWithData:data];
    // Compress by size
    NSUInteger lastDataLength = 0;
    while (data.length > maxLength && data.length != lastDataLength) {
        lastDataLength = data.length;
        CGFloat ratio = (CGFloat)maxLength / data.length;
        //NSLog(@"Ratio = %.1f", ratio);
        CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)),
                                 (NSUInteger)(resultImage.size.height * sqrtf(ratio))); // Use NSUInteger to prevent white blank
        UIGraphicsBeginImageContext(size);
        [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
        resultImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        data = UIImageJPEGRepresentation(resultImage, compression);
        //NSLog(@"In compressing size loop, image size = %ld KB", data.length / 1024);
    }
    //NSLog(@"After compressing size loop, image size = %ld KB", data.length / 1024);
    return data;
}

+ (UIImage *)scaleImage:(UIImage *)img withScaleFactor:(CGFloat)scaleFactor {
    
    CGSize size = CGSizeMake(img.size.width * scaleFactor, img.size.height * scaleFactor);
    
    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGAffineTransform transform = CGAffineTransformIdentity;
    
    transform = CGAffineTransformScale(transform, scaleFactor, scaleFactor);
    CGContextConcatCTM(context, transform);
    
    // Draw the image into the transformed context and return the image
    [img drawAtPoint:CGPointMake(0.0f, 0.0f)];
    UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return newimg;
}

+ (NSString *)calulateFileSize:(NSData *)data {
    
    if(!data){
        
        return [NSString stringWithFormat:@"0.00bytes"];
    }else{
        
        double dataLength = [data length] * 1.0;
        NSArray *typeArray = @[@"bytes",@"KB",@"MB",@"GB",@"TB",@"PB", @"EB",@"ZB",@"YB"];
        NSInteger index = 0;
        while (dataLength > 1024) {
            dataLength /= 1024.0;
            index ++;
        }
        
        return [NSString stringWithFormat:@"%.2f%@",dataLength,typeArray[index]];
    }
}


+ (float)cpuUsage {
    kern_return_t           kr;
    thread_array_t          thread_list;
    mach_msg_type_number_t  thread_count;
    thread_info_data_t      thinfo;
    mach_msg_type_number_t  thread_info_count;
    thread_basic_info_t     basic_info_th;
    
    kr = task_threads(mach_task_self(), &thread_list, &thread_count);
    if (kr != KERN_SUCCESS) {
        return -1;
    }
    float  cpu_usage = 0;
    
    for (int i = 0; i < thread_count; i++){
        thread_info_count = THREAD_INFO_MAX;
        kr = thread_info(thread_list[i], THREAD_BASIC_INFO,(thread_info_t)thinfo, &thread_info_count);
        if (kr != KERN_SUCCESS) {
            return -1;
        }
        
        basic_info_th = (thread_basic_info_t)thinfo;
        
        if (!(basic_info_th->flags & TH_FLAGS_IDLE)){
            cpu_usage += basic_info_th->cpu_usage;
        }
    }
    
    cpu_usage = cpu_usage / (float)TH_USAGE_SCALE * 100.0;
    
    vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
    
    return cpu_usage;
}

+ (long long)memoryUsageCount {
    int64_t memoryUsageInByte = 0;
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    kern_return_t kernelReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
    if(kernelReturn == KERN_SUCCESS) {
        memoryUsageInByte = (int64_t) vmInfo.phys_footprint;
    }
    return memoryUsageInByte;
}

+ (NSString *)memoryUsage {
    //同Xcode显示数据一致，但具体显示需要进行测试
    return [NSByteCountFormatter stringFromByteCount:[self memoryUsageCount] countStyle:NSByteCountFormatterCountStyleMemory] ? : @"";
}

+ (float)memoryUsedPercent{
    struct mach_task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
    if (kerr == KERN_SUCCESS){
        float used_bytes = info.resident_size;
        float total_bytes = [NSProcessInfo processInfo].physicalMemory;
        //NSLog(@"Used: %f MB out of %f MB (%f%%)", used_bytes / 1024.0f / 1024.0f, total_bytes / 1024.0f / 1024.0f, used_bytes * 100.0f / total_bytes);
        //转化为百分数
        return (used_bytes * 100.f) / total_bytes;
    }
    return 1;
}

+ (NSString *)currentNetworkType {
    AFNetworkReachabilityStatus status = [AFNetworkReachabilityManager sharedManager].networkReachabilityStatus;
    switch (status) {
        case AFNetworkReachabilityStatusUnknown:
            //未知
            return @"0";
        case AFNetworkReachabilityStatusNotReachable:
            //断网
            return @"9";
        case AFNetworkReachabilityStatusReachableViaWWAN:
            //流量
            return self.WWANNetType;
        case AFNetworkReachabilityStatusReachableViaWiFi:
            //WIFI
            return @"1";
        default:
            return @"0";
    }
}

+ (NSString *)WWANNetType{
    CTTelephonyNetworkInfo *info = [CTTelephonyNetworkInfo new];
    NSString *networkType = @"";
    if ([info respondsToSelector:@selector(currentRadioAccessTechnology)]) {
        NSString *currentStatus = info.currentRadioAccessTechnology;
        NSArray *network2G = @[CTRadioAccessTechnologyGPRS, CTRadioAccessTechnologyEdge, CTRadioAccessTechnologyCDMA1x];
        NSArray *network3G = @[CTRadioAccessTechnologyWCDMA, CTRadioAccessTechnologyHSDPA, CTRadioAccessTechnologyHSUPA, CTRadioAccessTechnologyCDMAEVDORev0, CTRadioAccessTechnologyCDMAEVDORevA, CTRadioAccessTechnologyCDMAEVDORevB, CTRadioAccessTechnologyeHRPD];
        NSArray *network4G = @[CTRadioAccessTechnologyLTE];
        //5(5G) 暂缺
        if ([network2G containsObject:currentStatus]) {
            //2G
            networkType = @"2";
        }else if ([network3G containsObject:currentStatus]) {
            //3G
            networkType = @"3";
        }else if ([network4G containsObject:currentStatus]){
            //4G
            networkType = @"4";
        }else {
            //未知网络
            networkType = @"0";
        }
    }
    return networkType;
}

+ (NSDictionary *)parseQueryString:(NSString *)query {
    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    NSArray *pairs = [query componentsSeparatedByString:@"&"];
    
    for (NSString *pair in pairs) {
        
        NSArray *elements = [pair componentsSeparatedByString:@"="];
        
        if(elements.count > 0){
            
            NSString *key = [[elements objectAtIndex:0] stringByRemovingPercentEncoding] ? : @"";
            if([key isKindOfClass:[NSNull class]]){
                continue;
            }
            
            NSString *val = @"";
            if(elements.count > 1){
                val = [[elements objectAtIndex:1] stringByRemovingPercentEncoding] ? : @"";
            }
            [dict setObject:val forKey:key];
        }
    }
    return dict;
}

+ (void)dispatchAsyncOnMainThread:(void (^)(void))block{
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

+ (void)keyChainSaveToService:(NSString *)service withData:(id)data{

    NSMutableDictionary *keychainQuery = [self keyChainFetchKeychainQuery:service];
    //删除旧有数据
    SecItemDelete((CFDictionaryRef)keychainQuery);
    //添加新数据到查询服务中
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
    //保存到keyChain
    SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
}

+ (id)keyChainLoadByService:(NSString *)service{
    id ret = nil;
    
    NSMutableDictionary *keychainQuery = [self keyChainFetchKeychainQuery:service];
    
    //查询设定
    
    //kSecReturnData 因暂时只返回一个数据，所以设定为True
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
    [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
    
    CFDataRef keyData = NULL;
    if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
        @try {
            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
        } @catch (NSException *e) {
            NSLog(@"Unarchive of %@ failed: %@", service, e);
        } @finally {
        }
    }
    if (keyData){
        CFRelease(keyData);
    }
    return ret;
}

+ (void)keyChainDeleteDataByService:(NSString *)service{
    NSMutableDictionary *keychainQuery = [self keyChainFetchKeychainQuery:service];
    SecItemDelete((CFDictionaryRef)keychainQuery);
}

+ (NSMutableDictionary *)keyChainFetchKeychainQuery:(NSString *)service {
    
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            
            (id)kSecClassGenericPassword,(id)kSecClass,
            
            service, (id)kSecAttrService,
            
            service, (id)kSecAttrAccount,
            
            (id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
            
            nil];
}

+(NSString *)callerStackStr{
    NSMutableString *mstr = [[NSMutableString alloc] init];
    void* callstack[128];
    int i, frames = backtrace(callstack, 128);
    char** strs = backtrace_symbols(callstack, frames);
    for (i = 0; i <frames; ++i) {
        [mstr appendFormat:@"%s\n", strs[i]];
    }
    return [NSString stringWithString:mstr] ? : @"";
}

+ (void)userDefaultStore:(_Nonnull id)obj forKey:(NSString * _Nonnull)key{
    NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
    [standardDefaults setObject:obj forKey:key];
    [standardDefaults synchronize];
}

+ (id)userDefaultObjForKey:(NSString * _Nonnull)key{
    return [[NSUserDefaults standardUserDefaults] objectForKey:key];
}

+ (BOOL)checkIsSameDate:(NSDate *)oldDate withDate:(NSDate *)newDate{
    
    NSCalendar* calendar = [NSCalendar currentCalendar];
    
    unsigned unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth |  NSCalendarUnitDay;
    
    NSDateComponents* oldComponents = [calendar components:unitFlags fromDate:oldDate];
    NSDateComponents* newComponents = [calendar components:unitFlags fromDate:newDate];
    
    return [oldComponents day]   == [newComponents day] &&
    [oldComponents month] == [newComponents month] &&
    [oldComponents year]  == [newComponents year];
}

+(NSArray<NSArray *> *)dividSourceArr:(NSArray *)srcArr byStepLength:(NSUInteger)stepLength{
    
    if(![srcArr isKindOfClass:[NSArray class]]
       ||srcArr.count == 0
       ||stepLength == 0){
        return [NSArray arrayWithArray:srcArr];
    }
    NSMutableArray *resultArr = [NSMutableArray array];
    NSUInteger itemsRemaining = srcArr.count;
    
    int j = 0;
    while(itemsRemaining) {
        NSRange range = NSMakeRange(j, MIN(stepLength, itemsRemaining));
        NSArray *subArr = [srcArr subarrayWithRange:range];
        [resultArr addObject:subArr];
        
        itemsRemaining -= range.length;
        j += range.length;
    }
    return (NSArray<NSArray *> *)[NSArray arrayWithArray:resultArr];
}

+ (NSString *)MD5In32BitFromStr:(NSString *)srcString{
    const char *cStr = [srcString UTF8String];
    unsigned char result[16];
    CC_MD5(cStr, (CC_LONG)strlen(cStr), result); // This is the md5 call
    return [NSString stringWithFormat:
            @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
            result[0], result[1], result[2], result[3],
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15]
            ];
}

@end
